干货|《超智能体》作者会津博士YJango:循环神经网络—scan实现LSTM
来源:知乎 作者:YJango
相关文章:
干货|《超智能体》作者会津博士YJango:如何利用循环神经网络实现LSTM?
【超智能体】循环神经网络(Recurrent)与前馈网络的差别及优劣
【AI技术】超智能体:人工神经网络●梯度下降训练法
github原文阅读地址:《循环神经网络——代码LV1 》(https://yjango.gitbooks.io/superorganism/content/%E4%BB%A3%E7%A0%81%E6%BC%94%E7%A4%BA.html)
注:从《人工神经网络》(https://yjango.gitbooks.io/superorganism/content/ren_gong_shen_jing_wang_luo.html)后的内容就需要逐步阅读,没有相关知识而跳跃阅读是难以理解的。
上一节在《循环神经网络——实现LSTM》(https://yjango.gitbooks.io/superorganism/content/lstmgru.html)中介绍了循环神经网络目前最流行的实现方法LSTM和GRU,这一节就演示如何利用Tensorflow来搭建LSTM网络。
代码LV1是指本次的演示是最核心的code,并没有多余的功能。
为了更深刻的理解LSTM的结构,这次所用的并非是tensorflow自带的rnn_cell类,而是从新编写,并且用scan来实现graph里的loop (动态RNN)。
任务描述
这次所要学习的模型依然是代码演示LV3 (https://yjango.gitbooks.io/superorganism/content/%E4%BB%A3%E7%A0%81%E6%BC%94%E7%A4%BAlv3.html)中的用声音来预测口腔移动,没有阅读的朋友请先阅读链接中的章节对于任务的描述。同时拿链接中的前馈神经网络与循环神经网络进行比较。
处理训练数据
目的:减掉每句数据的平均值,除以每句数据的标准差,降低模型拟合难度。
代码:
示意图:1,2,3,4,5表示list中的每个元素,而每个元素又是一个长度为2的list。
解释:比如全部数据有100个序列,如果设定每个input的形状就是[1, n_steps, D_input],那么处理后的list的长度就是100,这样的数据使用的是SGD的更新方式。而如果想要使用mini-batch GD,将batch size(也就是n_samples)的个数为2,那么处理后的list的长度就会是50,每次网络训练时就会同时计算2个样本的梯度并用均值来更新权重。 因为每句语音数据的时间长短都不相同,如果使用3维tensor,需要大量的zero padding,所以将n_samples设成1。但是这样处理的缺点是:只能使用SGD,无法使用mini-batch GD。如果想使用mini-batch GD,需要几个n_steps长度相同的样本并在一起形成3维tensor(不等长时需要zero padding,如下图)。
演示图:v表示一个维度为39的向量,序列1的n_steps的长度为3,序列2的为7,如果想把这三个序列并成3维tensor,就需要选择最大的长度作为n_steps的长度,将不足该长度的序列补零(都是0的39维的向量)。最后会形成shape为[3,7,39]的一个3维tensor。
权重初始化方法
目的:合理的初始化权重,可以降低网络在学习时卡在鞍点或极小值的损害,增加学习速度和效果。
代码:
解释:其中shufflelists是用于洗牌重新排序list的。正交矩阵初始化是有利于gated_rnn的学习的方法。
定义LSTM类
属性:使用class类来定义是因为LSTM中有大量的参数,定义成属性方便管理。
代码:在init中就将所有需要学习的权重全部定义成属性
解释:将hidden state和memory并在一起,以及将输入的形状变成[n_steps, n_samples, D_cell]是为了满足tensorflow中的scan的特点,后面会提到。
每步计算方法:定义一个function,用于制定每一个step的计算。
代码:
解释:将上一时刻的hidden state和memory拆开,用于计算后,所出现的新的当前时刻的hidden state和memory会再次并在一起作为该function的返回值,同样是为了满足scan的特点。定义该function后,LSTM就已经完成了。one_step方法会使用LSTM类中所定义的parameters与当前时刻的输入和上一时刻的hidden state与memory cell计算当前时刻的hidden state和memory cell。
scan:使用scan逐次迭代计算所有timesteps,最后得出所有的hidden states进行后续的处理。
代码:
解释:scan接受的fn, elems, initializer有以下要求:
fn:第一个输入是上一时刻的输出(需要与fn的返回值保持一致),第二个输入是当前时刻的输入。
elems:scan方法每一步都会沿着所要处理的tensor的第一个维进行一次一次取值,所以要将数据由[n_samples, n_steps, D_cell]的形状变成[n_steps, n_samples, D_cell]的形状。
initializer:初始值,需要与fn的第一个输入和返回值保持一致。
scan的返回值在上例中是[n_steps, 2, n_samples, D_cell],其中第二个维度的2是由hidden state和memory cell组成的。
构建网络
代码:
解释:以hard coding的方式直接构建一个网络,输入是39维,第一个隐藏层也就是RNN-LSTM,1024维,而输出层又将1024维的LSTM的输出变换到24维与label对应。
注: 这个网络并不仅仅取序列的最后一个值,而是要用所有timestep的值与实际轨迹进行比较计算loss
训练网络
代码:
解释:由于上文的LSTM是非常直接的编写方式,并不高效,在实际使用中会花费较长时间。
预测效果
代码:
解释:plot出一个样本中的维度的预测效果与真是轨迹进行对比
效果图:
总结说明
该文是尽可能只展示LSTM最核心的部分(只训练了10次,有兴趣的朋友可以自己多训练几次),帮助大家理解其工作方式而已,完整代码可以从我的github中LSTM_lV1中找到。
该LSTM由于运行效率并不高,下一篇会稍微进行改动加快运行速度,并整理结构方便使用GRU以及多层RNN的堆叠以及双向RNN,同时加入其他功能。
GitHub资源:https://github.com/YJango/tensorflow/blob/master/RNN/LSTM_scan_LV1.ipynb